<?php
/**************************************************************
 *
 *
使用特定function对数组中所有元素做处理
 * @param string &$array 要处理的字符串
 * @param string $function 要执行的函数
 * @return boolean $apply_to_keys_also 是否也应用到key上
 * @access public
 *
 *************************************************************/
function arrayRecursive(&$array, $function, $apply_to_keys_also = false) {
    static $recursive_counter = 0;
    if (++$recursive_counter > 1000) {
        die('possible deep recursion attack');
    }
    foreach ($array as $key => $value) {
        if (is_array($value)) {
            arrayRecursive($array[$key], $function, $apply_to_keys_also);
        } else {
            $array[$key] = $function($value);
        }
        if ($apply_to_keys_also && is_string($key)) {
            $new_key = $function($key);
            if ($new_key != $key) {
                $array[$new_key] = $array[$key];
                unset($array[$key]);
            }
        }
    }
    $recursive_counter--;
}

/**************************************************************
 *
 * 将数组转换为JSON字符串（兼容中文）
 * @param array $array 要转换的数组
 * @return string 转换得到的json字符串
 * @access public
 *
 *************************************************************/
function JSON($array) {
    arrayRecursive($array, 'urlencode', true);
    $json = json_encode($array);
    return urldecode($json);
}

/***********************************************************
 * 截取字符串，
 * @param $string
 * @param $sublen 截取长度
 * @param int $start
 * @param string $code
 * @return string
 */
function cut_str($string, $sublen, $start = 0, $code = 'UTF-8')
{
    if ($code == 'UTF-8') {
        $pa = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|\xe0[\xa0-\xbf][\x80-\xbf]|[\xe1-\xef][\x80-\xbf][\x80-\xbf]|\xf0[\x90-\xbf][\x80-\xbf][\x80-\xbf]|[\xf1-\xf7][\x80-\xbf][\x80-\xbf][\x80-\xbf]/";
        preg_match_all ( $pa, $string, $t_string );
        if (count ( $t_string [0] ) - $start > $sublen)
            return join ( '', array_slice ( $t_string [0], $start, $sublen ) ) ;
        return join ( '', array_slice ( $t_string [0], $start, $sublen ) );
    } else {
        $start = $start * 2;
        $sublen = $sublen * 2;
        $strlen = strlen ( $string );
        $tmpstr = '';
        for($i = 0; $i < $strlen; $i ++) {
            if ($i >= $start && $i < ($start + $sublen)) {
                if (ord ( substr ( $string, $i, 1 ) ) > 129) {
                    $tmpstr .= substr ( $string, $i, 2 );
                } else {
                    $tmpstr .= substr ( $string, $i, 1 );
                }
            }
            if (ord ( substr ( $string, $i, 1 ) ) > 129)
                $i ++;
        }
        if (strlen ( $tmpstr ) < $strlen)
            $tmpstr .= '';
        return $tmpstr;
    }
}

/**
 * @param $time
 * @param string $fomart
 * @param string $str
 * @return false|string
 */
function getFomartDate($time=NOW_TIME, $fomart='Y-m-d H:i:s', $str=''){
    switch ($fomart) {
        case 'd+':
            $fomart = 'Y-m-d 00:00:00'; break;
        case 'd':
            $fomart = 'Y-m-d'; break;
        case 'm+':
            $fomart = 'Y-m-01 00:00:00'; break;
        case 'm':
            $fomart = 'Y-m-01'; break;
    }
    if (!empty($str)) {
        $tmp = strtotime($str, $time);
        return date($fomart, $tmp);
    }
    return date($fomart, $time);
}

function getDjsBytime($time, $formart = 'd天h小时i分'){
    $str = 0;
    if ($time>0){
        $d = floor($time/86400);
        $las = $time % 86400;
        $h = floor($las / 3600);
        $las = $las % 3600;
        $i = floor($las / 60);
        $formart = str_replace('d', $d, $formart);
        $formart = str_replace('h', $h, $formart);
        $formart = str_replace('i', $i, $formart);
        $str = $formart;
    }
    return $str;
}



/**
 * @param $string 明文 或 密文
 * @param string $operation  DECODE表示解密,其它表示加密
 * @param string $key 密匙
 * @param int $expiry 密文有效期 (秒)
 * @return string
 */
function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
    // 动态密匙长度，相同的明文会生成不同密文就是依靠动态密匙
    $ckey_length = 4;
    
    // 密匙
    $key = md5 ( $key ? $key : 'boo' );
    
    // 密匙a会参与加解密
    $keya = md5 ( substr ( $key, 0, 16 ) );
    // 密匙b会用来做数据完整性验证
    $keyb = md5 ( substr ( $key, 16, 16 ) );
    // 密匙c用于变化生成的密文
    $keyc = $ckey_length ? ($operation == 'DECODE' ? substr ( $string, 0, $ckey_length ) : substr ( md5 ( microtime () ), - $ckey_length )) : '';
    // 参与运算的密匙
    $cryptkey = $keya . md5 ( $keya . $keyc );
    $key_length = strlen ( $cryptkey );
    // 明文，前10位用来保存时间戳，解密时验证数据有效性，10到26位用来保存$keyb(密匙b)，解密时会通过这个密匙验证数据完整性
    // 如果是解码的话，会从第$ckey_length位开始，因为密文前$ckey_length位保存 动态密匙，以保证解密正确
    $string = $operation == 'DECODE' ? base64_decode ( substr ( $string, $ckey_length ) ) : sprintf ( '%010d', $expiry ? $expiry + time () : 0 ) . substr ( md5 ( $string . $keyb ), 0, 16 ) . $string;
    $string_length = strlen ( $string );
    $result = '';
    $box = range ( 0, 255 );
    $rndkey = array ();
    // 产生密匙簿
    for($i = 0; $i <= 255; $i ++) {
        $rndkey [$i] = ord ( $cryptkey [$i % $key_length] );
    }
    // 用固定的算法，打乱密匙簿，增加随机性，好像很复杂，实际上对并不会增加密文的强度
    for($j = $i = 0; $i < 256; $i ++) {
        $j = ($j + $box [$i] + $rndkey [$i]) % 256;
        $tmp = $box [$i];
        $box [$i] = $box [$j];
        $box [$j] = $tmp;
    }
    // 核心加解密部分
    for($a = $j = $i = 0; $i < $string_length; $i ++) {
        $a = ($a + 1) % 256;
        $j = ($j + $box [$a]) % 256;
        $tmp = $box [$a];
        $box [$a] = $box [$j];
        $box [$j] = $tmp;
        // 从密匙簿得出密匙进行异或，再转成字符
        $result .= chr ( ord ( $string [$i] ) ^ ($box [($box [$a] + $box [$j]) % 256]) );
    }
    if ($operation == 'DECODE') {
        // substr($result, 0, 10) == 0 验证数据有效性
        // substr($result, 0, 10) - time() > 0 验证数据有效性
        // substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16) 验证数据完整性
        // 验证数据有效性，请看未加密明文的格式
        if ((substr ( $result, 0, 10 ) == 0 || substr ( $result, 0, 10 ) - time () > 0) && substr ( $result, 10, 16 ) == substr ( md5 ( substr ( $result, 26 ) . $keyb ), 0, 16 )) {
            return substr ( $result, 26 );
        } else {
            return '';
        }
    } else {
        // 把动态密匙保存在密文里，这也是为什么同样的明文，生产不同密文后能解密的原因
        // 因为加密后的密文可能是一些特殊字符，复制过程可能会丢失，所以用base64编码
        return $keyc . str_replace ( '=', '', base64_encode ( $result ) );
    }
}